package borders;

import squares.Direction;
import squares.Square;
import dungeons.Dungeon;
import be.kuleuven.cs.som.annotate.*;
/**
 * a class of borders that specifies a border of a square.
 * @Invar each Border can have his first attached square as an attached square.
 * 			| isValidAttachedSquare(getAttachedSquare1())
 * @Invar each Border can have his second attached square as an attached square.
 * 			| isValidAttachedSquare(getAttachedSquare2())
 * @Invar each Border can have can not have his first attached square as his second
 * 			|if(getAttachedSquare1() != null)
 * 			|	!getAttachedSquare1().equals(getAttachedSquare2())
 * @version 1.0
 * @author Gilles Groven & Jochen Tombal
 */
public abstract class Border {
	
	/**
	 *Variable registering the first square which this border is attached to, initial set to null
	 */
	private Square attachedSquare1 = null;
	/**
	 *Variable registering the second square which this border is attached to, initial set to null
	 */
	private Square attachedSquare2 = null;
	
	
	/**
	 * Initializes this border with the square it is attached to
	 * @param square1
	 * 		The (initial) square to which this border is attached to
	 * @effect the initial attached square of this border is set to the given square
	 * 		 | setAttachedSquare1(square1)
	 * @post	the given square is the square attached to this border
	 * 		 | new.getAttachedSquare1() == square1;
	 * @post  this border is not attached to a second square
	 * 		 | new.getAttachedSquare2() == null;
	 */
	@Raw
	@Model
	protected Border(Square square1)
	{
		setAttachedSquare1(square1);
	}
	/**
	 * Initializes this border with the two square it is attached to.
	 * @param square1
	 * 		The first square to which this border is attached to.
	 * @param square2
	 * 		the second square to which this border is attached to.
	 * @effect the first attached square to which this border is attached to is set to the given square.
	 * 		| setAttachedSquare1(square1)
	 * @effect the second attached square to which this border is attached to is set to the given square,
	 * 			or to zero depending if this square is valid.
	 * 		| setAttachedSquare2(square2)
	 * @post the first attached square to which this border is attached to is set to the given square.
	 * 		| new.getAttachedSquare1() == square1
	 * @post the second attached square to which this border is attached to is set to the given square,
	 * 		or left unchanged (meaning it will be null) if not valid as a border
	 * 		| if(!this.isValidAttachedSquare(square2))
	 * 		|	then new.getAttachedSquare2() == null
	 * 		| else  new.getAttachedSquare2() == square2	
	 */
	protected Border(Square square1,Square square2)
	{
		setAttachedSquare1(square1);
		if(!square1.equals(square2))
			setAttachedSquare2(square2);	
	}
	/**
	 * initializes the border with no attached squares.
	 * @post the border has no attached squares.
	 * 		| new.getAttachedSquare1() == null;
	 * 		| new.getAttachedSquare2() == null; 
	 */
	protected Border()
	{
		
	}

	/**
	 * sets the first attached square of this border to the given square
	 * @param square1
	 * 		the new square this border has to be attached to
	 * @post
	 * 		the new first attached square this border is attached to is equal
	 * 		to the given square or left unchanged if not valid
	 * 		| if(!this.isValidAttachedSquare(square1)
	 * 		|	then new.getAttachedSquare1() == this.getAttachedSquare1()
	 * 		| else
	 * 			new.getAttachedSquare1() == square1
	 * @note	private method, so no other class can illegally overwrite a square related to a border
	 */
	@Raw
	private void setAttachedSquare1(Square square1)
	{
		if(isValidAttachedSquare(square1))
		{
			this.attachedSquare1 = square1;
		}
	}
	/**
	 * sets the second attached square of this border to the given square.
	 * @param square2
	 * 		the new second square this border has to be attached to.
	 * @post
	 * 		the new second attached square this border is attached to is equal
	 * 		to the given square or left unchanged if not valid
	 * 		| if(!isValidAttachedSquare(square2)
	 * 		|	then new.getAttachedSquare2() == this.getAttachedSquare2();
	 * 		| else
	 * 		|	new.getAttachedSquare2() == square2;
	 *  @note	private method, so no other class can illegally overwrite a square related to a border
	 */
	@Raw
	private void setAttachedSquare2(Square square2)
	{
		if(isValidAttachedSquare(square2))
		{
			this.attachedSquare2 = square2;
		}
	}
	/**
	 * checks if the given square is a valid attachment for this border
	 * @param square
	 * 			the square that has to be checked.
	 * @return
	 * 			true if the square is valid, meaning it doesn't equal an other attached square.
	 * 			| if(( !square.equals(this.getAttachedSquare1()) && !square.equals(this.getAttachedSquare2()) || square == null)
	 * 			|	then result == true;
	 * 			| else
	 * 			|	result  == false;
	 */
	private boolean isValidAttachedSquare(Square square)
	{
		if(square == null)
			return true;
		return !square.equals(getAttachedSquare1()) && !square.equals(getAttachedSquare2());
	}
	/**
	 * @return
	 * 		 the first square this border is attached to.
	 */
	@Basic @Raw
	public Square getAttachedSquare1()
	{
		return this.attachedSquare1;
	}
	/**
	 * @return
	 * 		the second square this border is attached to.
	 */
	@Basic @Raw
	public Square getAttachedSquare2()
	{
		return this.attachedSquare2;
	}
	/**
	 * checks if the border has only one square attached
	 * @return
	 * 		true if one of the attached squares equals zero
	 * 		| result == this.getAttachedSquare1() == null || this.getAttachedSquare2() == null
	 * @note a border can only be constructed with at least one square attached.
	 */
	public boolean hasOnlyOneSquareAttached()
	{
		if(getAttachedSquare1() == null || getAttachedSquare2() == null)
		{
			return true;
		}
	return false;
	}
	/**
	 * returns a integer to specify where you can attach a new square.
	 * @return
	 * 		returns 1 if square1 is free, 2 if square2 is free, and 0 if no square is avaible.
	 * 		| if(this.getAttachedSquare1()== null)
	 * 		| 	then return == 1
	 * 		| else if(this.getAttachedSquare2()==null)
	 * 		| 	then return == 2
	 * 		|else
	 * 		| 	return == 0
	 */
	private int getFreeAttachSpace()
	{
		if(getAttachedSquare1()== null)
			return 1;
		else if(getAttachedSquare2()==null)
			return 2;
		else
			return 0;
	} 
	/**
	 * this method will set the attached square of a border.
	 * @param direction
	 * 			the direction where the border will be placed in the square, this is used to check if the square
	 * 			does indeed have this border as a border in that direction.
	 * @param square
	 * 			the square you want to attach.
	 * @post one of the attached squares  is the newly attached square, or the border is left unchanged if the square
	 * 		didn't had this border as a border.
	 * 		| if((!square.equals(this.getAttachedSquare1()) || !square.equals(this.getAttachedSquare2()))
	 * 		|		&& square.getBorderAt(direction).equals(this))
	 * 		| 	then new.getAttachedSquare1.equals(square) || new.getAttachedSquare2.equals(square)
	 */
	public void setAttachedSquareTo(Direction direction,Square square)
	{
		if(square.equals(getAttachedSquare1()) || square.equals(getAttachedSquare2()))
		{
		}
		else
		{
			if(square.getBorderAt(direction).equals(this))
			{
				if(getFreeAttachSpace() == 0)
				{
					
				}
				else if(getFreeAttachSpace() == 1)
				{
					this.setAttachedSquare1(square);
				}
				else
				{
					this.setAttachedSquare2(square);
				}
			}
		}
	}
	
	/**
	 * deletes the reference of a border to a square
	 * @param 	direction
	 * 			The direction of a square where this border is attached.
	 * @param 	square
	 * 			The square which reference to is to be deleted
	 * @post	The given square is not attached to this border
	 * 			|!square.equals(new.getAttachedSquare1()) || !square.equals(new.getAttachedSquare2()) || square == null
	 */
	public void deleteAttachedSquare(Direction direction,Square square)
	{
		if(!square.equals(getAttachedSquare1()) || !square.equals(getAttachedSquare2()) || square == null)
		{
		}
		else
		{
			int i = 0;
			for(Direction directionCheck: Direction.values())
			{
				if(square.getBorderAt(directionCheck).equals(this))
					i++;
			}
			if(!square.getBorderAt(direction).equals(this) && i == 0)
			{
				if(square.equals(getAttachedSquare1()))
					setAttachedSquare1(null);
				else
					setAttachedSquare2(null);
			}
		}
		
	}
	/**
	 * removes the given square from this borders attached squares, used in the dungeon when removing a square
	 * @param 	square
	 * 			the square you want to remove
	 * @param	x
	 * 			the x coordinate where you can find this square in the given dungeon
	 * @param 	y
	 * 			the y coordinate where you can find this square in the given dungeon
	 * @param 	z
	 * 			the z coordinate where you can find this square in the given dungeon
	 * @param	dungeon
	 * 			the dungeon in which this square is located
	 * @post	if the square was located at the given location in the dungeon, the border will have no reference to the given square
	 * 			| new.getAttachedSquare1() != square && new.getAttachedSquare2() != square;
	 * @note 	the coordinates functions as a private key since there is no method in dungeon
	 * 			that returns these coordinates and a square can't know his coordinates. Thus if
	 * 			the given coordinates matches the given square this method is called from Dungeon.
	 * @note 	if the border has only this square attached it is not yet deleted 
	 * 			since the square still refers to this border. The next step is detach the reference
	 * 			of the square to this border. The invariant of Border is still valid.
	 */
	public void detachSquare (Square square, long x, long y, long z, Dungeon<? extends Square> dungeon)
	{
		Boolean deleteRight = dungeon.getSquareAt(x, y, z).equals(square);
		if(!square.equals(getAttachedSquare1()) || !square.equals(getAttachedSquare2()))
		{
		}
		else if(deleteRight)
		{
			if(square.equals(getAttachedSquare1()))
				this.attachedSquare1 = null;
			else if (square.equals(getAttachedSquare2()))
				this.attachedSquare2 = null;
		}
		
	}
	/**
	 * gets the other square that is attached to this border, if the given square is attached to this border
	 * @param square
	 * 		the square you don't want to find.
	 * @return null if the square wasn't valid, and the square attached to this border if it is valid.
	 * 		|   if(this.getAttachedSquare1().equals(square))
	 * 		|		then result == this.getAttachedSquare1() 
	 * 		|   else if(this.getAttachedSquare2().equals(square))
	 * 		|   	then result == this.getAttachedSquare2()
	 * 		|   else 
	 * 		|		result == null
	 */
	public Square getOtherAttachedSquare(Square square)
	{
		Square rSquare = null;
		if(getAttachedSquare1() != null)
		{
			if(getAttachedSquare1().equals(square))
				rSquare  = getAttachedSquare2();
		}
		 if(getAttachedSquare2() != null)
		{
				if(getAttachedSquare2().equals(square))
					rSquare = getAttachedSquare1();
		}
		 return rSquare;
	}
	
	
	
	
}
